#include <QApplication>
#include <QClipboard>
#include <QGridLayout>
#include <QPushButton>
#include <QRegularExpression>

#include <botan/auto_rng.h>
#include <botan/hash.h>
#include <botan/base64.h>
#include <botan/hex.h>

#include <login_and_registration/registrationdialog.h>
#include <utils/hashes.h>

#include "utils/functions.h"

namespace login_and_registration {
registrationDialog::registrationDialog(QWidget *parent) : QDialog(parent){
	QGridLayout* formGridLayout = new QGridLayout( this );

	editUserName = new QLineEdit(this);
	editUserName->setFocus();
	editPassword = new QLineEdit(this);
	editPassword->setEchoMode(QLineEdit::PasswordEchoOnEdit);
	auto *action = editPassword->addAction(QIcon(":/resources/generatePasswordBlue.png"), QLineEdit::TrailingPosition);

	connect(action, SIGNAL(triggered(bool)), this, SLOT(generatePassword()));
	edit2ndPassword = new QLineEdit( this );
	edit2ndPassword->setEchoMode( QLineEdit::Password );
	edit2ndPassword->setEnabled(false);

	// initialize the labels
	labelUsername = new QLabel( this );
	labelPassword = new QLabel( this );
	label2ndPassword = new QLabel(this);
	labelUsername->setText( tr( "Логин" ) );
	labelUsername->setBuddy( editUserName );
	labelPassword->setText( tr( "Пароль" ) );
	labelPassword->setBuddy( editPassword );
	label2ndPassword->setText("Пароль повторно");
	label2ndPassword->setBuddy(edit2ndPassword);
	passwordErrorLabel = new QLabel(this);
	passwordErrorLabel->setText("Пароли не совпадают");
	passwordErrorLabel->setVisible(false);
	passwordErrorLabel->setStyleSheet("QLabel {color : red;}");
	QLabel *algorithmLabel = new QLabel("Алгоритм:", this);
	algorithmCombo = new QComboBox(this);
	algorithmCombo->addItems(QStringList() << "Кузнечик" << "Магма(ГОСТ-89)" << "AES-256");

	// initialize buttons
	buttons = new QDialogButtonBox( this );
	buttons->addButton(QDialogButtonBox::Cancel );
	buttons->addButton(QDialogButtonBox::Ok );
	buttons->button(QDialogButtonBox::Ok )->setText("Создать пользователя");
	buttons->button(QDialogButtonBox::Cancel)->setText("Отменить создание пользователя");


	connect(editPassword, SIGNAL(textChanged(const QString &)), this, SLOT(isPasswordSecure(const QString&)));
	connect(edit2ndPassword, SIGNAL(textChanged(const QString &)), this, SLOT(checkPasswords()));
	connect(buttons->button( QDialogButtonBox::Ok ),
			SIGNAL (clicked()), this, SLOT (createUser()));
	connect(buttons->button( QDialogButtonBox::Cancel ),
			SIGNAL (clicked()), this, SLOT (reject()));

	// place components into the dialog
	formGridLayout->addWidget( labelUsername, 0, 0 );
	formGridLayout->addWidget( editUserName, 0, 1 );
	formGridLayout->addWidget( labelPassword, 1, 0 );
	formGridLayout->addWidget( editPassword, 1, 1 );
	formGridLayout->addWidget(label2ndPassword, 2,0);
	formGridLayout->addWidget(edit2ndPassword, 2,1);
	formGridLayout->addWidget(passwordErrorLabel,3,1);
	formGridLayout->addWidget(algorithmLabel, 4,0);
	formGridLayout->addWidget(algorithmCombo, 4,1);
	formGridLayout->addWidget(buttons, 5, 0, 1, 3 );

	setLayout( formGridLayout );
	setWindowTitle("Регистрация пользователя");
	passwordsAreSame = false;
}



void registrationDialog::checkPasswords() {
	// Получаем введенные пароли из QLineEdit
	QString password1 = editPassword->text();
	QString password2 = edit2ndPassword->text();

	// Проверяем, совпадают ли пароли
	if (password1 == password2) {
		if(passwordErrorLabel->isVisible())
			passwordErrorLabel->setVisible(false);
		passwordsAreSame = true;
	} else {
		passwordErrorLabel->setVisible(true);
		passwordErrorLabel->setText("Пароли не совпадают");
		passwordsAreSame = false;
	}
}

void registrationDialog::isPasswordSecure(const QString& password) {
	QRegularExpression regex("[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?]");
	if (password.length() < 8) {
		passwordIsSecure = false;
		passwordErrorLabel->setText("Пароль должен содержать не менее 8 символов.");
		passwordErrorLabel->setVisible(true);
		edit2ndPassword->setEnabled(false);
	} else if (!password.contains(QRegularExpression("\\d"))) {
		passwordIsSecure = false;
		passwordErrorLabel->setText("Пароль должен содержать хотя бы одну цифру.");
		passwordErrorLabel->setVisible(true);
		edit2ndPassword->setEnabled(false);
	} else if (!password.contains(QRegularExpression("[A-Z]"))) {
		passwordIsSecure = false;
		passwordErrorLabel->setText("Пароль должен содержать хотя бы одну заглавную букву.");
		passwordErrorLabel->setVisible(true);
		edit2ndPassword->setEnabled(false);
	} else if (!password.contains(QRegularExpression("[a-z]"))) {
		passwordIsSecure = false;
		passwordErrorLabel->setText("Пароль должен содержать хотя бы одну строчную букву.");
		passwordErrorLabel->setVisible(true);
		edit2ndPassword->setEnabled(false);
	} else if (!password.contains(regex)) {
		passwordIsSecure = false;
		passwordErrorLabel->setText("Пароль должен содержать хотя бы один специальный символ.");
		passwordErrorLabel->setVisible(true);
		edit2ndPassword->setEnabled(false);
	} else if (utils::getHashes().contains(password.toStdString())) {
		passwordIsSecure = false;
		passwordErrorLabel->setText("Пароль содержится в базе нерекомендуемых паролей.");
		passwordErrorLabel->setVisible(true);
		edit2ndPassword->setEnabled(false);
	} else {
		passwordIsSecure = true;
		passwordErrorLabel->setVisible(false);
		edit2ndPassword->setEnabled(true);
	}
}

Botan::secure_vector<uint8_t> generate_salt(size_t salt_size) {
	Botan::AutoSeeded_RNG rng;
	Botan::secure_vector<uint8_t> salt(salt_size);
	rng.randomize(salt.data(), salt.size());
	return salt;
}


void registrationDialog::createUser() {
	if(passwordsAreSame && passwordIsSecure) {
		users::user user;
		user._login = editUserName->text().toStdString();
		auto generatedSalt = generate_salt(16);
		std::string salt_str = Botan::base64_encode(generatedSalt.data(), generatedSalt.size());
		user._salt =salt_str;
		user.setHashedPassword(utils::generateSymmetricKeyFromPassword(editPassword->text().toStdString()+salt_str, 32));
		user._dataPath = editUserName->text().toStdString() + ".data";
		user._totpPath = editUserName->text().toStdString() + "_totp"+".data";
		user._cipher_algorithm_= utils::createAlgos(algorithmCombo->currentText().toStdString());
		emit sendUser(user);
		close();
	}
}

void registrationDialog::generatePassword() {
	Botan::AutoSeeded_RNG rng;

	// Определяем набор символов, из которых будет состоять пароль
	const std::string charset =
		"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+";

	std::string password;
	password.reserve(16);

	for (int i = 0; i < 16; ++i) {
		// Генерируем случайное число для выбора символа из набора
		uint8_t random_byte = rng.next_byte();
		// Нормализуем случайное число в диапазоне от 0 до длины набора символов
		size_t index = random_byte % charset.size();
		password += charset[index];
	}

	_pop_up = new utils::PopUp(QWidget::geometry(), this);
	_pop_up->setPopupText("Пароль скопирован");
	_pop_up->show();
	QApplication::clipboard()->setText(QString::fromStdString(password));
	editPassword->setText(QString::fromStdString(password));
	edit2ndPassword->setText(QString::fromStdString(password));
	emit sendDataCopied();
	QTimer *timer = new QTimer(this);
	editPassword->setEchoMode(QLineEdit::Normal);

	// Подключаем сигнал timeout к лямбда-функции, которая будет менять EchoMode
	connect(timer, &QTimer::timeout, [=]() {
		// При срабатывании таймера меняем режим отображения текста в QLineEdit
			editPassword->setEchoMode(QLineEdit::Password);
			if(QApplication::clipboard()->text() ==  QString::fromStdString(password)) {
				QApplication::clipboard()->clear();
			}
	});

	// Запускаем таймер, например, каждые 5 секунд
	timer->start(5000); // 5000 миллисекунд = 5 секунд
}
}
